/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.anhndt.audiobook.business;

import com.anhndt.audiobook.entity.ItemPartEntity;
import com.anhndt.audiobook.iface.IMediaListener;
import com.anhndt.audiobook.iface.ProcessListener;
import com.anhndt.audiobook.utils.StringUtil;
import com.anhndt.audiobook.utils.Util;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Vector;
import javax.microedition.media.Manager;
import javax.microedition.media.MediaException;
import javax.microedition.media.Player;
import javax.microedition.media.PlayerListener;
import javax.microedition.media.control.VolumeControl;

/**
 *
 * @author Sun
 */
public class MediaPlayerControl implements ProcessListener, PlayerListener {

    private static final int LEVEL_VOLUME = 100;
    private static final int LEVEL_INCREMENT_VOLUME = 10;
    public static boolean autonext = true;
    private int maxBlock;
    // timer (for time display and karaoke asynchronous event)
    private final int TIMER_INTERVAL = 1000;
    private Player nextPlayerTemp;
    private Player nextPlayer;
    private Player curPlayer;
    // display timer
    private Timer guiTimer = null;
    private TimerTask timeDisplayTask = null;
    private IMediaListener mediaListener;
    //1 Mang chua cac ByteArrayInputStream
    private ByteArrayInputStream[] audios;
    private byte[] header = null;
    private float loadingProgress;
    private int ticking;
    private static int volume = 100;
    private boolean paused, muted;
    //index the hien xem dang choi den block bao nhieu trong Vector audios
    //gia tri the hien xem da download den block thu bao nhieu trong mang audios
    private int indexBlockDownloaded = -1, indexBlockPlaying = 0, indexPlaying, duration;
    private long partSize;
    private Vector mAudioItems;
    private ItemPartEntity mItemPlaying;
    static MediaPlayerControl mInstance;
    private AudioDownloadManager mAudioDownloadManager;

    public MediaPlayerControl getInstance() {
        return mInstance;
    }

    public MediaPlayerControl(IMediaListener listener, Vector audioItems, int indexPlaying) {
        mInstance = this;
        this.indexPlaying = indexPlaying;
        this.mediaListener = listener;
        this.mAudioItems = audioItems;

        updatePlaying();
    }

    public void setMediaPlayerListener(IMediaListener listener) {
        this.mediaListener = listener;
    }

    void updatePlaying() {
        mItemPlaying = (ItemPartEntity) mAudioItems.elementAt(indexPlaying);
        duration = mItemPlaying.getDuration();
        partSize = mItemPlaying.getPartSize();
//        System.out.println("$$$$$$$$$$$$$$$$$$$$$   " + itemPlaying.getPartUrl() + "   " + itemPlaying.other + " $$$$$$$$$$$$$$$$$$$$$");
    }

    public boolean start() {
        updatePlaying();

        Util.lastIndexArticlePartID = indexPlaying;
        Util.lastArticlePartID = mItemPlaying.id;
        Util.lastArticleEntity = mItemPlaying.getParent();
        mItemPlaying.setPlaying(true);
        mItemPlaying.changeMarked(true);
        //caculate counter of block
        int counterBlock = Util.divRounded2(partSize, AudioDownloadManager.SIZE_BLOCK);
        audios = new ByteArrayInputStream[counterBlock];
        if (guiTimer == null) {
            guiTimer = new Timer();
        }

        maxBlock = Math.min(counterBlock, 2);
        if (maxBlock == 0) {
            if (mediaListener != null) {
                mediaListener.downloading(false);
            }
            return false;
        }
        startDownloadAudio();
        if (mediaListener != null) {
            mediaListener.updateStatus();
        }
        return true;
    }

    public void startDownloadAudio() {
        //TODO test 
//        String url = "http://192.168.1.148:81/audiows/audiofile.aspx?u=1&a=6&p=224";
        if(mAudioDownloadManager != null) {
            mAudioDownloadManager.release();
        }
        String url = StringUtil.format(Util.AUDIO_FILE_URL,
                new String[]{Util.userID, mItemPlaying.getParent().id, mItemPlaying.id});
        System.out.println("[startDownloadAudio] " + url);
        mAudioDownloadManager = new AudioDownloadManager(url, partSize, this);
        try {
            mAudioDownloadManager.loadHeader();
            mAudioDownloadManager.startLoadBlock(maxBlock);
        } catch (IOException ex) {
            ex.printStackTrace();
        }
        if (mediaListener != null) {
            mediaListener.downloading(true);
        }
    }

    public void stopLoadBlockAudio() {
        if (mediaListener != null) {
            mediaListener.downloading(false);
        }
        if (mAudioDownloadManager != null) {
            mAudioDownloadManager.stop();
            mAudioDownloadManager = null;
        }
    }

    public void next() {
        next(autonext);
    }

    public void prev() {
        prev(autonext);
    }

    public void next(boolean start) {
        indexPlaying = indexPlaying < mAudioItems.size() - 1 ? indexPlaying + 1 : 0;
        release();
        updatePlaying();
        if (mediaListener != null) {
            mediaListener.updateStatus();
        }
        if (start) {
            start();
        } else if (mediaListener != null) {
            mediaListener.updateStatus();
        }

    }

    public void prev(boolean start) {
        indexPlaying = indexPlaying > 0 ? indexPlaying - 1 : mAudioItems.size() - 1;
        release();
        updatePlaying();
        if (mediaListener != null) {
            mediaListener.updateStatus();
        }
        if (start) {
            start();
        } else if (mediaListener != null) {
            mediaListener.updateStatus();
        }

    }

    /**
     * Tinh toan so luong block se download tiep
     * @return
     */
    private int calUrlsDownloading() {
        String[] s = null;
        int numberBlockInMemory = getNumberBlockInAudios(); //So luong block con trong mang audios
        int numberBlockRemainDownload = audios.length - 1 - indexBlockDownloaded; // so luong block can fai download tiep
        int numberBlockDownloading = Math.min(Math.abs(maxBlock - numberBlockInMemory), maxBlock); //So luong block se duoc download trong luot tiep theo
        if (numberBlockDownloading > numberBlockRemainDownload) {
            numberBlockDownloading = numberBlockRemainDownload;
        }
        return numberBlockDownloading;
    }

    public int getIndexPlaying() {
        return indexPlaying;
    }

    public ItemPartEntity getItemPlaying() {
        return mItemPlaying;
    }

    public int size() {
        return mAudioItems.size();
    }

    /**
     *
     * @return url block
     */
    private String getUrlBlock(String partID, int indexBlock) {
        return StringUtil.format(Util.AUDIO_FILE_URL,
                new String[]{Util.userID, mItemPlaying.getParent().id, partID, "" + ++indexBlock});
    }

    /**
     * Lay ra so luong block con luu tru trong mang audios
     * @return
     */
    public int getNumberBlockInAudios() {
        int counter = 0;
        for (int i = 0; i < audios.length; i++) {
            if (audios[i] != null) {
                counter++;
            }
        }
        return counter;
    }

    public int getTicking() {
        return ticking;
    }

    public void release() {
        indexBlockDownloaded = - 1;
        indexBlockPlaying = ticking = 0;
        paused = false;
        mItemPlaying.setPlaying(false);
        mItemPlaying.setPaused(false);
        stopAllMediaPlayer();
        stopLoadBlockAudio();
        audios = null;
        mAudioDownloadManager = null;
    }

    /**
     * stop all player
     */
    public void stopAllMediaPlayer() {
        stopMediaPlayer(curPlayer);
        stopMediaPlayer(nextPlayerTemp);
        stopMediaPlayer(nextPlayer);
    }

    private void stopMediaPlayer(Player player) {
        if (player != null) {
            try {
                player.close();
//                player.deallocate();
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
        player = null;
    }

    public void mute() {
        muted = true;
        updateVolume(0);
    }

    public void sound() {
        muted = false;
        updateVolume();
    }

    public boolean pause() {
        if (curPlayer != null) {
            try {
                curPlayer.stop();
                mItemPlaying.setPlaying(false);
                mItemPlaying.setPaused(true);
                paused = true;
            } catch (MediaException ex) {
                ex.printStackTrace();
                paused = false;
            }
        }
        return paused;
    }

    public boolean resume() {
        boolean resumed = false;
        if (curPlayer != null && !isEndOfSong()) {
            try {
                curPlayer.start();
                resumed = true;
                paused = false;
            } catch (MediaException ex) {
                ex.printStackTrace();
                resumed = false;
            }
        }
        return resumed;
    }

    public boolean isPaused() {
        return paused;
    }

    public boolean isPlaying() {
        return !paused && curPlayer != null && curPlayer.getState() == Player.STARTED;
    }

    public boolean isDownloading() {
        return (mAudioDownloadManager != null && mAudioDownloadManager.isLoading());
    }

    public boolean isFinishDownloaded() {
        return (mAudioDownloadManager != null && mAudioDownloadManager.isFinished());
    }

    public synchronized void startDisplayTimer() {
        if (timeDisplayTask == null) {
            System.out.println("startDisplayTimer : " + ticking);
            timeDisplayTask = new SPTimerTask();
            guiTimer.scheduleAtFixedRate(timeDisplayTask, 0, TIMER_INTERVAL);
        }
    }

    public synchronized void stopDisplayTimer() {
        System.out.println("stopDisplayTimer");
        if (timeDisplayTask != null) {
            ticking = Math.max(ticking, 0);
            timeDisplayTask.cancel();
            timeDisplayTask = null;
            updateTime();
        }
    }

    void startPlayer(Player player) {
        try {
            player.start();
        } catch (Exception e) {
            if (!isPlaying() && mediaListener != null) {
                release();
                mediaListener.exception(IMediaListener.MEDIA_EXCEPTION);
            }
        }
    }

    private synchronized void playAudio(int index) {
//        System.out.println("play Audio : " + index);
        indexBlockPlaying = index;
        try {
            nextPlayerTemp = Manager.createPlayer(audios[index], "audio/mpeg");
            nextPlayerTemp.addPlayerListener(this);
            nextPlayerTemp.realize();
            nextPlayerTemp.prefetch();
            nextPlayerTemp.start();
            curPlayer = nextPlayerTemp;
        } catch (MediaException ex) {
            if (!isPlaying() && mediaListener != null) {
                release();
                mediaListener.exception(IMediaListener.MEDIA_EXCEPTION);
            }
        } catch (Exception e) {
        }
    }

    public boolean isEndOfSong() {
        return audios != null && indexBlockPlaying >= audios.length - 1;
    }

    private VolumeControl getVolumeControl() {
        return (curPlayer != null && curPlayer.getState() == Player.STARTED) ? (VolumeControl) curPlayer.getControl("VolumeControl") : null;
    }

    public static void incrementVolume() {
        volume += LEVEL_INCREMENT_VOLUME;
        if (volume > LEVEL_VOLUME) {
            volume = LEVEL_VOLUME;
        }
        if (mInstance != null) {
            mInstance.updateVolume();
        }
    }

    public static void decrementVolume() {
        volume -= LEVEL_INCREMENT_VOLUME;
        if (volume < 0) {
            volume = 0;
        }
        if (mInstance != null) {
            mInstance.updateVolume();
        }
    }

    private void updateProgress() {
//        System.out.println("updateProcess");
        if (curPlayer != null) {
            ticking++;
            if (mediaListener != null && ticking % 2 == 0) {
                mediaListener.updateProgress(ticking * 1000 / duration);
            }
        }
    }

    public void updateVolume() {
        if (muted) {
            updateVolume(0);
        } else {
            updateVolume(volume);
        }
    }

    public void updateVolume(int volume) {
        VolumeControl vc = getVolumeControl();
        if (vc != null) {
            vc.setLevel(volume);
        }
        if (mediaListener != null) {
            mediaListener.updateVolume(volume / 10);
        }
    }

    private void updateTime() {
//        System.out.println("updateTime");
        if (mediaListener != null) {
            mediaListener.updateTime();
        }
    }

    private synchronized void createPlayerWithNextBlock() {
        if ((nextPlayer == null || nextPlayerTemp == null) && indexBlockDownloaded > indexBlockPlaying) {
            int tmp = indexBlockPlaying + 1;
            try {
                Player playerTemp = Manager.createPlayer(audios[indexBlockPlaying + 1], "audio/mpeg");
                playerTemp.realize();
                playerTemp.prefetch();
                playerTemp.addPlayerListener(this);
                if (nextPlayer == null) {
                    nextPlayer = playerTemp;
                } else if (nextPlayerTemp == null) {
                    nextPlayerTemp = playerTemp;
                }
            } catch (Exception e) {
                if (e instanceof IOException && mediaListener != null) {
                    mediaListener.exception(IMediaListener.IO_EXCEPTION);
                } else if (e instanceof MediaException && mediaListener != null) {
                    mediaListener.exception(IMediaListener.MEDIA_EXCEPTION);
                }
            }
        }
    }

    public String getMediaTimeStr() {
        try {
            return formatTime(ticking);
        } catch (IllegalStateException ise) {
        }
        return "00:00";
    }

    public String getMediaDuration() {
        try {
            return formatTime(duration);
        } catch (IllegalStateException ise) {
        }
        return "00:00";
    }

    public static int getVolume() {
        return volume;
    }

    public static void setVolume(int v) {
        volume = v;
        if (mInstance != null) {
            mInstance.updateVolume();
        }
    }

    /**
     * tra ra dinh dang --:--
     * @param time la do dai cua bai hat theo giay
     * @return
     */
    public static String formatTime(int time) {
        return formatNumber(time / 60, 2, true) + ":" + formatNumber((time % 60), 2, true);
    }

    public static String formatNumber(long num, int len, boolean leadingZeros) {
        StringBuffer ret = new StringBuffer(String.valueOf(num));
        if (leadingZeros) {
            while (ret.length() < len) {
                ret.insert(0, '0');
            }
        } else {
            while (ret.length() < len) {
                ret.append('0');
            }
        }
        return ret.toString();
    }

    public void onError(String status, int index) {
    }

    /**
     * Ham nay duoc goi khi 1 block audio download xong
     * Data nay se duoc day vao audios
     * dong thoi se tinh toan xem download tiep block nao nua
     * @param result la 1 mang byte chua data da download duoc
     * @param index
     */
    public synchronized void onSuccess(Object result, int index) {
        if (result == null) {
            return;
        }
        //System.out.println("[onSuccess] start method : " + data.length);

        byte[] dataBlock = (byte[]) result;
        //finished downloaded header
        if (index == -1) {
            header = dataBlock;
        } else {
            //increment
            indexBlockDownloaded++;
            loadingProgress += dataBlock.length;
            if (loadingProgress > partSize) {
                loadingProgress = partSize;
            }
            //combine data block
            byte[] realBlock = combineBytes(header, dataBlock);
            dataBlock = null;
            
            audios[indexBlockDownloaded] = new ByteArrayInputStream(realBlock);
            if (!isPlaying() && !paused) {
                playAudio(indexBlockPlaying);
            } else if (!isCreateNextPlayer()) {
                createPlayerWithNextBlock();
            }
            
            if (mediaListener != null) {
                float percentLoadingProgress = loadingProgress / partSize;
                mediaListener.updateSecondaryProcess(percentLoadingProgress);
            }
        }
    }

    public synchronized void playerUpdate(Player player, String event, Object eventData) {
        // if the event is that the player has started, show the form
        // but only if the event data indicates that the event relates to newly
        // stated player, as the STARTED event is fired even if a player is
        // restarted. Note that eventData indicates the time at which the start
        // event is fired.
        if (event.equals(STARTED)) {
            System.out.println("[playerUpdate] ------- START");
            startDisplayTimer();
            mItemPlaying.setPlaying(true);
            updateVolume();
            if (mediaListener != null) {
                mediaListener.downloading(false);
            }
            if (!isCreateNextPlayer()) {
                createPlayerWithNextBlock();
            }
        } else if (isStopMedia(event)) {
            System.out.println("[playerUpdate] ------- STOP MEDIA");
            stopDisplayTimer();
        } else if (isEndOfMedia(event) && player == curPlayer) {
            System.out.println("[playerUpdate] ------- END OF MEDIA");
            //remove block da play xong ra khoi audios
            releaseBuffer(indexBlockPlaying);
            if (curPlayer == nextPlayer) {
                nextPlayer.close();
                nextPlayer = null;
            } else if (curPlayer == nextPlayerTemp) {
                nextPlayerTemp.close();
                nextPlayerTemp = null;
            }
            curPlayer = null;
            if (isEndOfSong()) {
                stopDisplayTimer();
                if (mediaListener != null) {
                    mediaListener.endOfSong();
                }
                if (autonext) {
                    next();
                }
            } else {
                if ((nextPlayer != null || nextPlayerTemp != null)) {
                    if (nextPlayer != null) {
                        curPlayer = nextPlayer;
                    } else if (nextPlayerTemp != null) {
                        curPlayer = nextPlayerTemp;
                    }
//                        curPlayer.start();
                    startPlayer(curPlayer);
                    indexBlockPlaying++;
                    updateVolume();
                } else {
                    playAudio(indexBlockPlaying + 1);
                }
                if (!isPlaying()) {
                    stopDisplayTimer();
                }
                if (mAudioDownloadManager != null) {
                    int counterBlockDownloading = calUrlsDownloading();
                    mAudioDownloadManager.startLoadBlock(counterBlockDownloading);
                }
            }
        }
    }

    private void releaseBuffer(int index) {
        try {
            audios[index] = null;
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private boolean isEndOfMedia(String evt) {
        return evt.equalsIgnoreCase(END_OF_MEDIA);
    }

    private boolean isStopMedia(String evt) {
        return evt.equalsIgnoreCase(STOPPED);
    }

    boolean isCreateNextPlayer() {
        return nextPlayer != null && nextPlayerTemp != null;
    }

    private byte[] combineBytes(byte[] header, byte[] content) {
        byte[] combine = new byte[header.length + content.length];
        System.arraycopy(header, 0, combine, 0, header.length);
        System.arraycopy(content, 0, combine, header.length, content.length);

        return combine;
    }

    private class SPTimerTask extends TimerTask {

        public void run() {
//            System.out.println("Ticking : "  + curPlayer.getMediaTime());
            if (ticking <= duration) {
                updateTime();
                updateProgress();
            }
        }
    }
}
